package com.idanchuang.component.redis;

import io.lettuce.core.ClientOptions;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;

import java.time.Duration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 *
 * 修复redis集群模式下扩容异常问题
 *
 * @author yjy
 * @date 2019/11/27 11:09
 **/
@Configuration
@ConditionalOnProperty(name = "spring.redis.cluster.nodes")
public class ComponentRedisPoolAutoConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(ComponentRedisPoolAutoConfiguration.class);

    @Autowired
    private RedisProperties redisProperties;

    public GenericObjectPoolConfig<?> genericObjectPoolConfig(RedisProperties.Pool properties) {
        GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
        if (properties == null) {
            return config;
        }
        config.setMaxTotal(properties.getMaxActive());
        config.setMaxIdle(properties.getMaxIdle());
        config.setMinIdle(properties.getMinIdle());
        if (properties.getMaxWait() != null) {
            config.setMaxWaitMillis(properties.getMaxWait().toMillis());
        }
        return config;
    }

    @Primary
    @Bean(destroyMethod = "destroy")
    @ConditionalOnProperty(name = "spring.redis.lettuce.cluster.refresh.adaptive.enabled", havingValue = "false", matchIfMissing = true)
    public LettuceConnectionFactory lettuceConnectionFactory() {
        if (redisProperties.getCluster() == null) {
            throw new IllegalStateException("Your redis are not cluster, but found dc.redis.lettuce.cluster.refresh.adaptive.enabled: true, please use cluster redis or set dc.redis.lettuce.cluster.refresh.adaptive.enabled = false");
        }

        //开启 自适应集群拓扑刷新和周期拓扑刷新
        ClusterTopologyRefreshOptions clusterTopologyRefreshOptions =  ClusterTopologyRefreshOptions.builder()
                // 开启全部自适应刷新
                .enableAllAdaptiveRefreshTriggers() // 开启自适应刷新,自适应刷新不开启,Redis集群变更时将会导致连接异常
                // 自适应刷新超时时间(默认30秒)
                .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30)) //默认关闭开启后时间为30秒
                // 开周期刷新
                .enablePeriodicRefresh(Duration.ofSeconds(10))
                .build();
        // https://github.com/lettuce-io/lettuce-core/wiki/Client-Options
        ClientOptions clientOptions = ClusterClientOptions.builder()
                .topologyRefreshOptions(clusterTopologyRefreshOptions)
                .validateClusterNodeMembership(false)
                .build();
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
        builder.poolConfig(genericObjectPoolConfig(redisProperties.getJedis().getPool()));
        builder.clientOptions(clientOptions);

        LettucePoolingClientConfiguration clientConfig = builder.build();

        List<String> clusterNodes = redisProperties.getCluster().getNodes();
        Set<RedisNode> nodes = new HashSet<>();
        clusterNodes.forEach(address -> nodes.add(new RedisNode(address.split(":")[0].trim(), Integer.parseInt(address.split(":")[1]))));

        RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
        clusterConfiguration.setClusterNodes(nodes);
        clusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
        if (redisProperties.getCluster().getMaxRedirects() != null) {
            clusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
        }
        logger.info("lettuceConnectionFactory > for cluster init");
        return new LettuceConnectionFactory(clusterConfiguration, clientConfig);
    }

}
